package com.example.micrpay.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.example.common.constants.RedisKey;
import com.example.common.constants.YLBConstant;
import com.example.micrpay.utils.HttpUtil;
import com.example.micrpay.utils.Pkipair;
import jakarta.annotation.Nullable;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.example.api.model.RechargeRecord;
import org.example.api.model.User;
import org.example.api.service.RechargeService;
import org.example.api.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @description: 快钱支付服务
 * @author: uu
 * @data: 2023/7/23 下午 03:12
 * @version: v1.0
 */
@Service
public class KQService {
    @DubboReference(interfaceClass = UserService.class, version = "1.0")
    private UserService userService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @DubboReference(interfaceClass = RechargeService.class, version = "1.0")
    private RechargeService rechargeService;


    /*创快钱表单*/
    public Map<String, String> generateFormDate(Integer uid, String name, String phone, BigDecimal rechargeMoney) {
        Map<String, String> map = new HashMap<>();

        //人民币网关账号，该账号为11位人民币网关商户编号+01,该参数必填。
        String merchantAcctId = "1001214035601";//
        //编码方式，1代表 UTF-8; 2 代表 GBK; 3代表 GB2312 默认为1,该参数必填。
        String inputCharset = "1";
        //接收支付结果的页面地址，该参数一般置为空即可。
        String pageUrl = "";
        //服务器接收支付结果的后台地址，该参数务必填写，不能为空。
        String bgUrl = "http://localhost:9000/pay/kq/notify";
        //网关版本，固定值：v2.0,该参数必填。
        String version =  "v2.0";
        //语言种类，1代表中文显示，2代表英文显示。默认为1,该参数必填。
        String language =  "1";
        //签名类型,该值为4，代表PKI加密方式,该参数必填。
        String signType =  "4";
        //支付人姓名,可以为空。
        String payerName= name;
        //支付人联系类型，1 代表电子邮件方式；2 代表手机联系方式。可以为空。
        String payerContactType =  "2";
        //支付人联系方式，与payerContactType设置对应，payerContactType为1，则填写邮箱地址；payerContactType为2，则填写手机号码。可以为空。
        String payerContact =  phone;
        //指定付款人，可以为空
        String payerIdType =  "3";
        //付款人标识，可以为空
        String payerId =  String.valueOf(uid);
        //付款人IP，可以为空
        String payerIP =  "";
        //商户订单号，以下采用时间来定义订单号，商户可以根据自己订单号的定义规则来定义该值，不能为空。
        String orderId = "KQ" + generateOrderId();

        //订单金额，金额以“分”为单位，商户测试以1分测试即可，切勿以大金额测试。该参数必填。
        String orderAmount = rechargeMoney.multiply(new BigDecimal(100)).stripTrailingZeros().toPlainString();
        //订单提交时间，格式：yyyyMMddHHmmss，如：20071117020101，不能为空。
        String orderTime = new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(new java.util.Date());
        //快钱时间戳，格式：yyyyMMddHHmmss，如：20071117020101， 可以为空
        String orderTimestamp= new java.text.SimpleDateFormat("yyyyMMddHHmmss").format(new java.util.Date());;
        //商品名称，可以为空。
        String productName= "小山科技";
        //商品数量，可以为空。
        String productNum = "1";
        //商品代码，可以为空。
        String productId = "10000";
        //商品描述，可以为空。
        String productDesc = "理财充值";
        //扩展字段1，商户可以传递自己需要的参数，支付完快钱会原值返回，可以为空。
        String ext1 = "扩展1";
        //扩展自段2，商户可以传递自己需要的参数，支付完快钱会原值返回，可以为空。
        String ext2 = "扩展2";
        //支付方式，一般为00，代表所有的支付方式。如果是银行直连商户，该值为10-1或10-2，必填。
        String payType = "00";
        //银行代码，如果payType为00，该值可以为空；如果payType为10-1或10-2，该值必须填写，具体请参考银行列表。
        String bankId = "";
        //同一订单禁止重复提交标志，实物购物车填1，虚拟产品用0。1代表只能提交一次，0代表在支付不成功情况下可以再提交。可为空。
        String redoFlag = "0";
        //快钱合作伙伴的帐户号，即商户编号，可为空。
        String pid = "";

        // signMsg 签名字符串 不可空，生成加密签名串
        String signMsgVal = "";
        signMsgVal = appendParam(signMsgVal, "inputCharset", inputCharset, map);
        signMsgVal = appendParam(signMsgVal, "pageUrl", pageUrl, map);
        signMsgVal = appendParam(signMsgVal, "bgUrl", bgUrl, map);
        signMsgVal = appendParam(signMsgVal, "version", version, map);
        signMsgVal = appendParam(signMsgVal, "language", language, map);
        signMsgVal = appendParam(signMsgVal, "signType", signType, map);
        signMsgVal = appendParam(signMsgVal, "merchantAcctId",merchantAcctId, map);
        signMsgVal = appendParam(signMsgVal, "payerName", payerName, map);
        signMsgVal = appendParam(signMsgVal, "payerContactType",payerContactType, map);
        signMsgVal = appendParam(signMsgVal, "payerContact", payerContact, map);
        signMsgVal = appendParam(signMsgVal, "payerIdType", payerIdType, map);
        signMsgVal = appendParam(signMsgVal, "payerId", payerId, map);
        signMsgVal = appendParam(signMsgVal, "payerIP", payerIP, map);
        signMsgVal = appendParam(signMsgVal, "orderId", orderId, map);
        signMsgVal = appendParam(signMsgVal, "orderAmount", orderAmount, map);
        signMsgVal = appendParam(signMsgVal, "orderTime", orderTime, map);
        signMsgVal = appendParam(signMsgVal, "orderTimestamp", orderTimestamp, map);
        signMsgVal = appendParam(signMsgVal, "productName", productName, map);
        signMsgVal = appendParam(signMsgVal, "productNum", productNum, map);
        signMsgVal = appendParam(signMsgVal, "productId", productId, map);
        signMsgVal = appendParam(signMsgVal, "productDesc", productDesc, map);
        signMsgVal = appendParam(signMsgVal, "ext1", ext1, map);
        signMsgVal = appendParam(signMsgVal, "ext2", ext2, map);
        signMsgVal = appendParam(signMsgVal, "payType", payType, map);
        signMsgVal = appendParam(signMsgVal, "bankId", bankId, map);
        signMsgVal = appendParam(signMsgVal, "redoFlag", redoFlag, map);
        signMsgVal = appendParam(signMsgVal, "pid", pid, map);



        System.out.println(signMsgVal);
        Pkipair pki = new Pkipair();
        /*生成签名串*/
        String signMsg = pki.signMsg(signMsgVal);
        map.put("signMsg", signMsg);
        return map;
    }


    /*获取用户信息*/
    public User queryUse(Integer uid){
        User user = userService.queryById(uid);
        return user;
    }

    /*拼接传递get传递的参数重载*/
    private String appendParam(String returns, String paramId, String paramValue){
        return appendParam(returns, paramId, paramValue, null);
    }
    /*拼接传递get传递的参数*/
    private String appendParam(String returns, String paramId, String paramValue, Map<String, String> data) {
        if (returns != "") {
            if (paramValue != "") {

                returns += "&" + paramId + "=" + paramValue;
            }

        } else {

            if (paramValue != "") {
                returns = paramId + "=" + paramValue;
            }
        }
        if (data != null){
            data.put(paramId, paramValue);
        }
        return returns;
    }

    /*生成订单号*/
    private String generateOrderId(){
        // 唯一值
        String key = RedisKey.KEY_RECHARGE_ORDERID;
        // redis自增
        Long incr = stringRedisTemplate.boundValueOps(key).increment();
        String orderId = DateFormatUtils.format(new Date(), "yyyyMMddHHmmssSSS") + incr;
        return orderId;
    }

    /*添加充值记录*/
    public boolean addChargeRecord(BigDecimal rechargeMoney, Map<String, String> map) throws ParseException {
        RechargeRecord record = new RechargeRecord();
        record.setUid(Integer.valueOf(map.get("payerId")));
        record.setRechargeNo(map.get("orderId"));
        record.setRechargeStatus(YLBConstant.RECHARGE_STATUS_ING);
        record.setRechargeMoney(rechargeMoney);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        record.setRechargeTime(sdf.parse(map.get("orderTime")));
        record.setRechargeDesc("小山金融理财充值");
        record.setChannel("快钱");
        //
        int count = rechargeService.addRechargeRecord(record);
        if (count < 1){
            return false;
        }
        return true;
    }

    /*将订单号放到redis中*/
    public void addOrderIdToRedis(String orderId, Long orderTempStamp) {
        String key = RedisKey.KEY_ORDERID_ZSET;
        stringRedisTemplate.boundZSetOps(key).add(orderId, orderTempStamp);
    }

    /* 处理异步通知*/
    public void kqNotify(HttpServletRequest request) {
        String merchantAcctId = request.getParameter("merchantAcctId");
        String version = request.getParameter("version");
        String language = request.getParameter("language");
        String signType = request.getParameter("signType");
        String payType = request.getParameter("payType");
        String bankId = request.getParameter("bankId");
        String orderId = request.getParameter("orderId");
        String orderTime = request.getParameter("orderTime");
        String orderAmount = request.getParameter("orderAmount");
        String bindCard = request.getParameter("bindCard");
        if(request.getParameter("bindCard")!=null){
            bindCard = request.getParameter("bindCard");}
        String bindMobile="";
        if(request.getParameter("bindMobile")!=null){
            bindMobile = request.getParameter("bindMobile");}
        String dealId = request.getParameter("dealId");
        String bankDealId = request.getParameter("bankDealId");
        String dealTime = request.getParameter("dealTime");
        String payAmount = request.getParameter("payAmount");
        String fee = request.getParameter("fee");
        String ext1 = request.getParameter("ext1");
        String ext2 = request.getParameter("ext2");
        String payResult = request.getParameter("payResult");
        String aggregatePay = request.getParameter("aggregatePay");
        String errCode = request.getParameter("errCode");
        String signMsg = request.getParameter("signMsg");
        // 拼接签名计算的串
        String merchantSignMsgVal = "";
        merchantSignMsgVal = appendParam(merchantSignMsgVal,"merchantAcctId", merchantAcctId, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "version",version, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "language",language, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "signType",signType, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "payType",payType, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "bankId",bankId, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "orderId",orderId, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "orderTime",orderTime, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "orderAmount",orderAmount, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "bindCard",bindCard, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "bindMobile",bindMobile, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "dealId",dealId, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "bankDealId",bankDealId, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "dealTime",dealTime, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "payAmount",payAmount, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "fee", fee, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "ext1", ext1, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "ext2", ext2, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "payResult",payResult, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "aggregatePay",aggregatePay, null);
        merchantSignMsgVal = appendParam(merchantSignMsgVal, "errCode",errCode, null);

//        System.out.println("merchantSignMsgVal="+merchantSignMsgVal);
        Pkipair pki = new Pkipair();

        boolean flag = pki.enCodeByCer(merchantSignMsgVal, signMsg);
        System.out.println(flag);
        // 方便调试
        flag = true;
        if (flag){
            // 处理业务逻辑

            // 1. 判断商户是自己的商户吗
            // 2. 判断订单是否存在
            // 3. 判断金额是否一致
            // 4. 更新用户的资金
                // 5. 修改用户的充值状态
            // 6. 删除redis中处理过的订单号


            if("1001214035601".equalsIgnoreCase(merchantAcctId)){
                int result = rechargeService.handleKQNotify(orderId, payAmount, payResult);
                System.out.println("订单" + orderId + "- 结果是 ：" + result);
            }

        }else {
            System.out.println("订单验签失败" + orderId);
        }
        // 删除redis中的订单记录
        stringRedisTemplate.boundZSetOps(RedisKey.KEY_ORDERID_ZSET).remove(orderId);

    }

    /*调用快钱的查询接口*/
    public void handleQueryOrder() {
        // 1. 从redis中获取订单号
        Set<String> orderIds = stringRedisTemplate.boundZSetOps(RedisKey.KEY_ORDERID_ZSET).range(0, -1);

        orderIds.forEach(orderId ->{
            // 2. 每个订单号调用查询接口
            gateWayInitQuery(orderId);
        });

    }


    /**
     * 支付查询demo
     */
    private void gateWayInitQuery(String orderId){
        Map<String, Object> request = new HashMap<String, Object>();
        //固定值：1代表UTF-8;
        String inputCharset = "1";
        //固定值：v2.0 必填
        String version = "v2.0";
        //1代表Md5，2 代表PKI加密方式  必填
        String signType = "2";
        //人民币账号 membcode+01  必填
        String merchantAcctId = "1001214035601";//1001293267101（XIXMFISFG7RGDKQN） 1001217486601(5B5EQDQH2X7ERM9A)
        //固定值：0 按商户订单号单笔查询，1 按交易结束时间批量查询必填
        String queryType = "0";
        //固定值：1	代表简单查询 必填
        String queryMode = "1";
        //数字串，格式为：年[4 位]月[2 位]日[2 位]时[2 位]分[2 位]秒[2位]，例如：20071117020101
        String startTime = "";//20200525000000
        ////数字串，格式为：年[4 位]月[2 位]日[2 位]时[2 位]分[2 位]秒[2位]，例如：20071117020101
        String endTime = "";	//	20200527180000
        String requestPage = "";
        //String orderId = "W02012011000000112";	// 20200526180806TS
        String key = "27YKWKBKHT2IZSQ4";//XIXMFISFG7RGDKQN

        request.put("inputCharset", inputCharset);
        request.put("version", version);
        request.put("signType", signType);
        request.put("merchantAcctId", merchantAcctId);
        request.put("queryType", queryType);
        request.put("queryMode", queryMode);
        request.put("startTime", startTime);
        request.put("endTime", endTime);
        request.put("requestPage", requestPage);
        request.put("orderId", orderId);

        String message="";
        message = appendParam(message,"inputCharset",inputCharset, null);
        message = appendParam(message,"version",version);
        message = appendParam(message,"signType",signType);
        message = appendParam(message,"merchantAcctId",merchantAcctId);
        message = appendParam(message,"queryType",queryType);
        message = appendParam(message,"queryMode",queryMode);
        message = appendParam(message,"startTime",startTime);
        message = appendParam(message,"endTime",endTime);
        message = appendParam(message,"requestPage",requestPage);
        message = appendParam(message,"orderId",orderId);
        message = appendParam(message,"key",key);

        System.out.println("参与加签字段===" + message);

        Pkipair pki = new Pkipair();
        String sign = pki.signMsg(message);

        System.out.println("签名串signMsg===" + sign);

        request.put("signMsg", sign);

        System.out.println("请求json串===" + JSON.toJSONString(request));

        //sandbox提交地址
        String reqUrl = "https://sandbox.99bill.com/gatewayapi/gatewayOrderQuery.do";

        String response = "";

        try {

            response = HttpUtil.doPostJsonRequestByHttps(JSON.toJSONString(request), reqUrl, 3000, 8000);

            System.out.println("response = " + response);

            // 解析response
            if (StringUtils.isNotBlank(response)){
                Object detailObject = JSONObject.parseObject(response).get("orderDetail");
                // 把查询结果转为JSONArray
                if (detailObject != null){
                    JSONArray jsonArray = (JSONArray) detailObject;
                    JSONObject jsonObject = jsonArray.getJSONObject(0);
                    if (jsonObject != null){
                            // 处理结果和异步通知一样
                            int result = rechargeService.handleKQNotify(jsonObject.getString("orderId"),
                                    jsonObject.getString("payAmount"),
                                    jsonObject.getString("payResult"));
                            System.out.println("订单号" + orderId + "-处理结果是：" + result);

                    }
                }else {
                    // 查询订单错误
                    if (JSONObject.parseObject(response).getString("errCode") != null) {
                        // 将充值订单的状态修改为失败
                        rechargeService.modifyRechargeStatus(orderId);
                    }
                }
            }
            // 将redis中的中处理的订单号删除
            stringRedisTemplate.boundZSetOps(RedisKey.KEY_ORDERID_ZSET).remove(orderId);

        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
    }
}

